#ifndef STATE_H_
#define STATE_H_

#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <queue>
#include "Timer.h"
#include "Logger.h"
#include "Square.h"
#include "Location.h"

/*
    constants
*/
const int TDIRECTIONS = 4;
const char CDIRECTIONS[4] = {'N', 'E', 'S', 'W'};
const int DIRECTIONS[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };      //{N, E, S, W}
const int LARGEVAL = 0x0fffffff;

class PlayerQueueEntry {
public:
    int playerId;
    int numAnts;
public:
    PlayerQueueEntry(const int& id,const int& nAnts): playerId(id),numAnts(nAnts) {}
    bool operator < (const PlayerQueueEntry& other) const{
	return numAnts > other.numAnts; //more ants higher priority
    }
};

typedef std::priority_queue<PlayerQueueEntry> PlayerQueue;

class LocQueueEntry {
public:
    Location targetLoc;
    double priority;
public:
    LocQueueEntry(const Location& target,double targetPriority): targetLoc(target),priority(targetPriority) {}
    bool operator < (const LocQueueEntry& other) const {
	return priority < other.priority;
    }
};

typedef std::priority_queue<LocQueueEntry> TargetQueue;

/*
    struct to store current state information
*/


typedef std::map<Location,std::map<Location,int> > movehistory_t;
struct State
{
    /*
        Variables
    */
    int rows, cols,
        turn, turns,
        noPlayers;
    double attackradius, spawnradius, viewradius;
    double loadtime, turntime;
    std::vector<double> scores;
    bool gameover;
    int64_t seed;

    int visiblelocations;
    double knownLandPercent;
    std::vector<std::vector<Square> > grid;
    std::vector<std::vector<bool> > visibleGrid;
    std::vector<std::vector<int> > visitedHistory;
    std::vector<std::vector<Square> > gridCopy;
    std::vector<Location> ants, enemies, food,myHills,enemyHills,enemyHillsCache;
    std::map<Location,int> foodTrackingPathLengths;
    std::map<Location,int> foodHistory;
    //Should persist across turns. Tracks which ants were being sent
    //where. If the ant dies then it should be removed from this map
    //If the target changes then it should be updated.
    //This is useful to prevent oscillation of ants when moving
    //to frontline. Unless they get a better order, an ant moving to frontline
    //will continue to do so without oscillating.
    std::map<Location,Location> antDestinationMap;
    struct AntWalkDetails {
	int d;
	int followd;
	int turns;
	bool following;
    };
    std::map<Location,AntWalkDetails> antWalkMap;
    //Map of maintaining a list of positions that the ant at a particular
    //Location has already visited. Updated every turn and after every issue
    //order. Removed when the ant dies.
    movehistory_t antMoveHistory;

    Timer timer;
    std::vector<Location> frontLine;
    //ordered by list of players with highest strength up first
    //useful for target selection. We want to make sure stronger
    //players don't get stronger to prevent exponential growth.
    PlayerQueue playerQ;
    /*
        Functions
    */
    State();
    ~State();

    void setup();
    void reset();

    void makeMove(const Location &loc, int direction);


    //useful for evaluating future states.
    int mockMakeMove(const Location& loc,int direction);
    void unmakeMockMove(const Location &loc,int direction,int savedAnt);


    double distance(const Location &loc1, const Location &loc2) const;
    double manhattan_distance(const Location& loc1, const Location &loc2) const;
    Location getLocation(const Location &startLoc, int direction) const;

    //functions to translate world to map coordinates
    int mapX(const int& x) const {
	return (x<0?rows-abs(x%rows):x%rows);
    }
    
    int mapY(const int&y) const {
	return (y<0?cols-abs(y%cols):y%cols);
    }
    
    
    void updateVisionInformation();
    int  increasesVision(const Location& loc) const ;
    int  increaseVision(const Location& loc);
    void analyzeMap();
    bool isEdgeLocation(const int& row,const int& col) const;

    //All Pathfinding functions
    //path from loc1 to loc2
    bool findPath(const Location& loc1, const Location& loc2,
		  std::stack<int>& path,int& numNodesExplored
		  ) const;
    //Returns the closest untracked food
    bool findClosestFood(const Location& loc,Location& foodLoc,std::stack<int>& path,int& numNodesExplored) const;
    //Always returns the closest enemyHill 
    bool findClosestEnemyHill2(const Location& loc,Location& hillLoc,std::stack<int>& path,int& numNodesExplored) const;

    //Always returns the closest enemyHill 
    bool findClosestEnemyHill(const Location& loc,Location& hillLoc,std::stack<int>& path,int& numNodesExplored) const;
    //Always returns the closest enemy ant tracked or not
    bool findClosestEnemyAnt(const Location& loc,Location& antLoc,std::stack<int>& path,int& numNodesExplored) const;
    //Always returns the closest friendly ant except the one on loc itself.
    bool findClosestFriendlyAnt(const Location& loc,Location& antLoc,std::stack<int>& path,int& numNodesExplored) const;

    //All Query Functions
    //find all ants(friendly/enemy) within a certain radius 
    bool myHillInView(const Location& loc,const double& radius,Location& hillLoc) const ;
    bool findEnemyHillsInRadius(const Location& loc,const double& radius,std::vector<Location>& hillLocs) const;
    void findAntsInRadius(const Location& loc,const double& radius,std::vector<Location>& antList) const;
    void findFriendlyAntsInRadius(const Location& loc,const double& radius,std::vector<Location>& antList) const;
    void findEnemyAntsInRadius(const Location& loc,const double& radius,std::vector<Location>& antList) const;
    void findThisAntsEnemiesInRadius(const Location& loc,const double& radius,std::vector<Location>& antList,int player_id) const;
    void findFoodInRadius(const Location& loc, double radius,std::vector<Location>& foodList) const;
    bool inSameOrAdjacentQuadrant(const Location& loc1,const Location& loc2);
    bool findUntrackedFood(Location& foodLoc);
    void multiSourceBFSFindMove(const std::vector<Location>& ants,
                                std::map<Location,int>& antMoves) const;

private:
    void extractPath(const Location& startLoc,const Location& endLoc,const std::vector< std::vector<int> >& visited,std::stack<int>& path) const;
    
    int extractMove(const Location& ant,const std::vector < std::vector<int> > &visited) const;
    double calcLocPriority(const Location& loc);
};

std::ostream& operator<<(std::ostream &os, const State &state);
std::istream& operator>>(std::istream &is, State &state);

#endif //STATE_H_
